package com.zs.usc.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zs.usc.common.BusinessException;
import com.zs.usc.common.RestCode;
import com.zs.usc.configuration.userlogin.LoginConfig;
import com.zs.usc.constant.Global;
import com.zs.usc.entity.UserInfo;
import com.zs.usc.enums.YesOrNo;
import com.zs.usc.mapper.UserInfoMapper;
import com.zs.usc.service.IUserService;
import com.zs.usc.token.ITokenManager;
import com.zs.usc.utils.*;
import com.zs.usc.vo.CurrentUserInfo;
import com.zs.usc.vo.UserInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
import java.util.Optional;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author zs
 * @since 2021-11-11
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements IUserService {

    @Autowired
    private LoginConfig loginConfig;

    @Autowired
    private ITokenManager tokenManager;

    @Autowired
    private RedisUtils redisUtils;


    @Override
    public Optional<UserInfo> selectOptionalById(String userId) {
        return  this.lambdaQuery().eq(UserInfo::getIsDelete, YesOrNo.NO)
                .eq(UserInfo::getUserId, userId)
                .oneOpt();
    }

    @Override
    public void saveUser(UserInfo userInfo) {
        // 用户名唯一
        Optional<UserInfo> optUsrInfo = this.lambdaQuery()
                .select(UserInfo::getUserId)
                .eq(UserInfo::getUsername, userInfo.getUsername())
                .oneOpt();
        if (optUsrInfo.isPresent())
            throw new BusinessException(userInfo.getUsername() + ":用户已存在！");
        if (!this.save(userInfo)) {
            log.error("新增用户信息失败！" + userInfo.toString());
            throw new BusinessException("新增用户信息失败!");
        }
    }

    @Override
    public UserInfoVO simpleLogin(String username, String password, String ip) {
        // 判读是否存在用户
        UserInfo userInfo = this.lambdaQuery()
                .eq(UserInfo::getUsername, username)
                .eq(UserInfo::getPassword, EncryptUtils.md5Encode(password))
                .eq(UserInfo::getIsDelete, YesOrNo.NO)
                .oneOpt()
                .orElseThrow(() -> new BusinessException(RestCode.ERROR_ACCOUNT_LOGIN_ERROR));

//        // 判读密码是否正确
//        if (!EncryptUtils.md5Encode(password).equals(userInfo.getPassword())) {
//            throw new BusinessException(StatusCode.ERROR_PASSWORD_ERROR);
//        }

        CurrentUserInfo loginInfo = CurrentUserInfo.builder()
                .uid(userInfo.getUserId())
                .username(userInfo.getUsername())
                .groupId(userInfo.getGroupId())
                .build();


        //通过IP缓存token
//        String key = "AUTH:COMMON:TOKEN:"+ip+"_"+u.getId();
//        String rekey = "AUTH:COMMON:REFRESH:TOKEN:"+ip+"_"+u.getId();

        // 同一个用户只允许
        String key = "AUTH:COMMON:TOKEN:" + "_" + userInfo.getUserId();
        String token = this.createToken(key, loginInfo);
//        String reToken = this.createReToken(rekey, u.getId());
        return UserInfoVO.builder()
                .currentUserInfo(loginInfo)
                .authentication(token)
                .build();

    }


    public String createToken(String key, CurrentUserInfo u) {
//        u.setPassword(null);
        /**
         * 先从缓存中找到相应的token  如果没有或token已经失效 则重新创建
         * 防止频繁登入 创建大量token
         *
         * key--->token
         * token--->loginInfo
         *
         *
         */
        String token = null;
        if (redisUtils.hasKey(key)) { //先判断缓存里面是否存在token信息
            token = redisUtils.getStr(key);
            if (!redisUtils.hasKey(token)) {   //判断 token是否存在
                token = null;
            }
        }

        if (token == null) {
            token = "token:" + UUIDUtils.generate();
            // token -->loginInfo
            redisUtils.set(token, u, Global.TOKEN_EXPIRE);
            // key -->token
            redisUtils.setStr(key, token, Global.TOKEN_EXPIRE);  //缓存token
        } else {
            // 更新token有效器
            redisUtils.expire(key, Global.TOKEN_EXPIRE);
        }
        return token;
    }


    @Override
    public UserInfoVO login(String username, String password) {
        // 获取用户信息 同时判读是否删除
        UserInfo userInfo = this.lambdaQuery()
                .eq(UserInfo::getUsername, username)
                // 未删除
                .eq(UserInfo::getIsDelete, YesOrNo.NO)
                .oneOpt()
                .orElseThrow(() -> new BusinessException(RestCode.ERROR_ACCOUNT_NOT_EXIST));
        // 是否生效
        if (!isEffective(userInfo))
            throw new BusinessException(RestCode.ERROR_ACCOUNT_INVALID);
        // 是否被禁止
        if (YesOrNo.YES.equals(userInfo.getIsForbidden()))
            throw new BusinessException(RestCode.ERROR_USER_ACCOUNT_LOCKED);

        // 判读是否被锁定
        if (YesOrNo.YES.equals(userInfo.getIsLocked()) && LocalDateTime.now().isBefore(userInfo.getLockedTime())) {

            long locked = TimeUtils.betweenTwoTime(LocalDateTime.now(), userInfo.getLockedTime(), ChronoUnit.MINUTES);

            throw new BusinessException(RestCode.ERROR_USER_ACCOUNT_LOCKED.getCode(), String.format("账号已经被锁定，请在%d分钟后重试！", locked));
        }


        userInfo.setIsLocked(YesOrNo.NO);
        // 判读密码
        if (!Objects.equals(EncryptUtils.md5Encode(password), userInfo.getPassword())) {
            // 判读是否是锁定能登录第一次登录错误
            if (YesOrNo.YES.equals(userInfo.getIsLocked())) {
                userInfo.setIsLocked(YesOrNo.NO);
                userInfo.setErrcount(1);
                userInfo.setLastErrorTime(LocalDateTime.now());
                // 更新库
                this.saveOrUpdate(userInfo);
                throw new BusinessException(RestCode.ERROR_PASSWORD_ERROR);
            }
            // 非锁定登录
            Integer errcount = userInfo.getErrcount();
            errcount++;
            // 锁定账号
            if (errcount >= loginConfig.getErrorCount()) {
                userInfo.setErrcount(errcount);
                userInfo.setLastErrorTime(LocalDateTime.now());
                userInfo.setIsLocked(YesOrNo.YES);
                userInfo.setLockedTime(LocalDateTime.now().plusMinutes(loginConfig.getLockedTime()));
                // 更新记录
                this.saveOrUpdate(userInfo);
                throw new BusinessException(RestCode.ERROR_USER_ACCOUNT_LOCKED);
            }

            // 不锁定账号
            userInfo.setErrcount(errcount);
            userInfo.setLastErrorTime(LocalDateTime.now());
            userInfo.setIsLocked(YesOrNo.NO);
            this.saveOrUpdate(userInfo);
            throw new BusinessException(RestCode.ERROR_PASSWORD_ERROR);
        }


        // 验证通过

        // 1.登录成功信息放入缓存
        String token = TokenUtils.createToken();
        CurrentUserInfo currentUserInfo = CurrentUserInfo.builder()
                .uid(userInfo.getUserId())
                .username(userInfo.getUsername())
                .groupId(userInfo.getGroupId())
                .build();
        tokenManager.addToken(token, currentUserInfo);
        // 2.角色 菜单权限 放入缓存 // TODO: 2021/11/13
        return UserInfoVO.builder()
                .currentUserInfo(currentUserInfo)
                .authentication(token)
                .build();
    }

    private boolean isEffective(UserInfo userInfo) {
        LocalDateTime effectiveTime = userInfo.getEffectiveTime();
        LocalDateTime invalidationTime = userInfo.getInvalidationTime();
        if (effectiveTime != null && LocalDateTime.now().isBefore(effectiveTime)) {
            return false;
        }
        if (invalidationTime != null && LocalDateTime.now().isAfter(effectiveTime)) {

            return false;
        }
        return true;
    }
}
